package fish;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import java.util.Vector;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JSlider;
import javax.swing.JTextField;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

public class Aquarium {
	public static void main(String[] args) {
		new Controller();
	}
}

class Model {
	Vector<Point> fishPositions;
	Vector<Image> fishLooks;
	int nrOfFishesExistent;
	int sizex, sizey;
	int speed;
	String[] fishImagePaths = { "src/fish/bruce.gif", "src/fish/bubbles.gif",
			"src/fish/crush.gif", "src/fish/deb.gif", "src/fish/dory.gif",
			"src/fish/gill.gif", "src/fish/gurgle.gif", "src/fish/jaques.gif",
			"src/fish/marlin.gif", "src/fish/nemo.gif", "src/fish/peach.gif",
			"src/fish/squirt.gif" };
	// String[] fishImagePaths = { "./bruce.gif", "./bubbles.gif",
	// "./crush.gif", "./deb.gif", "./dory.gif",
	// "./gill.gif", "./gurgle.gif", "./jaques.gif",
	// "./marlin.gif", "./nemo.gif", "./peach.gif",
	// "./squirt.gif" };
	Image[] fishImages;

	Model() {
		nrOfFishesExistent = 0;
		fishPositions = new Vector<Point>();
		fishLooks = new Vector<Image>();
		sizex = 1024;
		sizey = 768;
		speed = 10;
		fishImages = new Image[fishImagePaths.length];
		for (int i = 0; i < fishImagePaths.length; ++i) {
			try {
				fishImages[i] = ImageIO.read(new File(fishImagePaths[i]));
			} catch (IOException e) {
			}
		}
		addRandomFish();
	}

	public void addFish(Image fishLook) {
		fishPositions.add(new Point(0, 0));
		fishLooks.add(fishLook);
		new Fish(this);
	}

	public void addRandomFish() {
		addFish(fishImages[(int) (Math.random() * fishImages.length)]);
	}
}

class Settings extends JDialog {
	Model m_Mod;
	JSlider slider;
	JTextField numberOfFishesToAddTextField;
	JButton addFishButton;
	int nrOfFishesToAdd;

	Settings(Model m, View owner) {
		super(owner, "Settings", true);
		m_Mod = m;
		setLayout(new FlowLayout());
		add(slider = new JSlider(JSlider.HORIZONTAL, 10, 400, 11) {
			{
				setPaintLabels(true);
				setPaintTicks(true);
				addChangeListener(new ChangeListener() {

					@Override
					public void stateChanged(ChangeEvent arg0) {
						m_Mod.speed = getValue();
						System.out.println("Speed set to " + m_Mod.speed);
					}

				});
			}

		});
		add(numberOfFishesToAddTextField = new JTextField("0") {
			{
				setColumns(10);
				addKeyListener(new KeyAdapter() {
					public void keyReleased(KeyEvent e) {
						nrOfFishesToAdd = Integer
								.parseInt(Settings.this.numberOfFishesToAddTextField
										.getText());
					}
				});
			}
		});
		add(addFishButton = new JButton("AddFish") {
			{
				addActionListener(new ActionListener() {
					public void actionPerformed(ActionEvent e) {
						for (int i = 0; i < nrOfFishesToAdd; ++i)
							m_Mod.addRandomFish();
					}
				});
			}
		});
		pack();
		setVisible(true);
	}
}

class View extends JFrame {
	Model m_Mod;

	JLayeredPane lp = new JLayeredPane();

	void initMenuBar() {
		setJMenuBar(new JMenuBar() {
			{
				add(new JMenu("Options") {
					{
						add(new JMenuItem("Settings") {
							{
								addActionListener(new ActionListener() {
									public void actionPerformed(ActionEvent e) {
										new Settings(m_Mod, View.this);
									}
								});
							}
						});
						add(new JMenu("Threads") {
							{
								add(new JMenuItem("Neues Aquarium") {
									{
										addActionListener(new ActionListener() {
											public void actionPerformed(
													ActionEvent e) {
												new Controller();
											}
										});
									}
								});

							}
						});
					}
				});
			}
		});
	}

	View(Model m) {
		m_Mod = m;
		initMenuBar();
		setLayout(new BorderLayout());
		lp.add(new Waves(m_Mod), 1);
		lp.add(new Water(m_Mod), 0);
		addComponentListener(new ComponentAdapter() {
			public void componentResized(ComponentEvent e) {
				m_Mod.sizex = View.this.getWidth() - getInsets().left
						- getInsets().right;
				m_Mod.sizey = View.this.getHeight() - getInsets().top
						- getInsets().bottom;
			}
		});
		add(lp, BorderLayout.CENTER);
		setSize(m_Mod.sizex, m_Mod.sizey);
		setVisible(true);
	}

}

class Water extends JComponent implements Runnable {
	Model m_Mod;
	Image m_ImgBuffer;

	Water(Model m) {
		m_Mod = m;
		setBounds(0, 0, m_Mod.sizex, m_Mod.sizey);
		Thread t = new Thread(this);
		t.start();
	}

	public void run() {
		while (true) {
			try {
				Thread.sleep(10);
			} catch (InterruptedException e) {
			}
			repaint();
		}
	}

	public void paintComponent(Graphics g) {
		for (int i = 0; i < m_Mod.fishPositions.size(); ++i)
//			g.drawRect(m_Mod.fishPositions.get(i).x,
//					m_Mod.fishPositions.get(i).y, 10,
//					10);
			g.drawImage(m_Mod.fishLooks.get(i), m_Mod.fishPositions.get(i).x,
					m_Mod.fishPositions.get(i).y, 60, 60, this);
	}

	public void update(Graphics g) {
		if (m_ImgBuffer == null) {
			m_ImgBuffer = createImage(getWidth(), getHeight());
		}
		Graphics bufferGraphics = m_ImgBuffer.getGraphics();
		bufferGraphics.clearRect(0, 0, getWidth(), getHeight());
		paint(bufferGraphics);
		g.drawImage(m_ImgBuffer, 0, 0, this);
	}
}

class Waves extends JComponent {
	Model m_Mod;

	Waves(Model m) {
		m_Mod = m;
		setBounds(0, 0, m.sizex, m.sizey);
	}

	public void paintComponent(Graphics g) {
		try {
			g.drawImage(ImageIO.read(new File("src/fish/waves.jpeg")), 0, 0,
					m_Mod.sizex, m_Mod.sizey, this);
		} catch (IOException e) {
		}
	}
}

class Controller {
	Model m_Mod;
	View m_View;

	Controller() {
		m_Mod = new Model();
		m_View = new View(m_Mod);
	}
}

class Fish extends Thread {
	int fishIndex;
	Model m_Mod;

	String direction = "up";

	Fish(Model m) {
		m_Mod = m;
		fishIndex = m_Mod.nrOfFishesExistent++;
		m.fishPositions.get(fishIndex).x = (int) (Math.random() * m_Mod.sizex);
		m.fishPositions.get(fishIndex).y = (int) (Math.random() * m_Mod.sizey);
		start();
	}

	void setDirectionRandomly() {
		int randomNumber = (int) (Math.random() * 4 + 1);
		if (randomNumber == 1)
			direction = "up";
		if (randomNumber == 2)
			direction = "right";
		if (randomNumber == 3)
			direction = "down";
		if (randomNumber == 4)
			direction = "left";
	}

	void move() {
		if (direction == "up")
			moveUp();
		if (direction == "down")
			moveDown();
		if (direction == "left")
			moveLeft();
		if (direction == "right")
			moveRight();
	}

	void moveToRandomDirection() {
		while (true) {
			try {
				setDirectionRandomly();
				for (int i = 0; i < (int) (Math.random() * m_Mod.sizey); ++i) {
					move();
					Thread.sleep(50);
				}
			} catch (InterruptedException e) {

			}
		}
	}

	void moveUp() {
		if (m_Mod.fishPositions.get(fishIndex).y > 0)
			--m_Mod.fishPositions.get(fishIndex).y;
		else
			moveDown();
	}

	void moveDown() {
		if (m_Mod.fishPositions.get(fishIndex).y < m_Mod.sizey)
			++m_Mod.fishPositions.get(fishIndex).y;
		else
			moveUp();
	}

	void moveLeft() {
		if (m_Mod.fishPositions.get(fishIndex).x > 0)
			--m_Mod.fishPositions.get(fishIndex).x;
		else
			moveRight();
	}

	void moveRight() {
		if (m_Mod.fishPositions.get(fishIndex).x < m_Mod.sizex)
			++m_Mod.fishPositions.get(fishIndex).x;
		else
			moveLeft();
	}

	public void run() {
		while (true) {
			try {
				Thread.sleep(m_Mod.speed);
			} catch (InterruptedException e) {
			}
			moveToRandomDirection();
		}
	}
}
